﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Soporte
{
    public class Soporte
    {
        private static Soporte _instance;
        public static Soporte Instance
        {
            get { return _instance ?? (_instance = new Soporte()); }
        }

        private Keyboard _keyBoard;
        private Mouse _mouse;

        private KeyboardHook _keyboardHook;
        private FocusHook _focusHook;

        IntPtr desktop = IntPtr.Zero,
                hDcDesktop = IntPtr.Zero,
                hDcLupa = IntPtr.Zero;
        
        public Soporte()
        {
            _keyBoard = new Keyboard();
            _mouse = new Mouse();

            _keyboardHook = new KeyboardHook();
            _focusHook = new FocusHook();

            desktop = NativeMethods.GetDesktopWindow();
            hDcDesktop = NativeMethods.GetWindowDC(desktop);
            hDcLupa = NativeMethods.CreateCompatibleDC(hDcDesktop);
        }

        public Keyboard GetKeyboard()
        {
            if(_keyBoard == null) _keyBoard = new Keyboard();
            return _keyBoard;
        }
        
        public Mouse GetMouse()
        {
            if (_mouse == null) _mouse = new Mouse();
            return _mouse;
        }

        public FocusHook GetControlFoco()
        {
            if (_focusHook == null) _focusHook = new FocusHook();
            return _focusHook;
        }

        public KeyboardHook GetControlTeclado()
        {
            if (_keyboardHook == null) _keyboardHook = new KeyboardHook();
            return _keyboardHook;
        }
       
        #region Font smoothing
        const uint SPI_GETFONTSMOOTHING = 74;
        const uint SPI_SETFONTSMOOTHING = 75;
        const uint SPIF_UPDATEINIFILE = 0x1;

        public Boolean GetFontSmoothing()
        {
            int pv = 0;
            /* Call to systemparametersinfo to get the font smoothing value. */
            bool iResult = NativeMethods.SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, ref pv, 0);
            if (pv > 0)
            {
                //pv > 0 means font smoothing is on.
                return true;
            }
            else
            {
                //pv == 0 means font smoothing is off.
                return false;
            }
        }

        public void DisableFontSmoothing()
        {
            int pv = 0;
            /* Call to systemparametersinfo to set the font smoothing value. */
            bool iResult = NativeMethods.SystemParametersInfo(SPI_SETFONTSMOOTHING, 0, ref pv, SPIF_UPDATEINIFILE);
        }

        public void EnableFontSmoothing()
        {
            int pv = 0;
            /* Call to systemparametersinfo to set the font smoothing value. */
            bool iResult = NativeMethods.SystemParametersInfo(SPI_SETFONTSMOOTHING, 1, ref pv, SPIF_UPDATEINIFILE);
        }
        #endregion

        #region Alto contraste
        public void SetAltoContraste(bool enable)
        {
            var hcf = new NativeMethods.HIGHCONTRAST();

            if (enable)
            {

                hcf.cbSize = NativeMethods.HIGHCONTRAST.GetSize();
                hcf.dwFlags = NativeMethods.HCF_HIGHCONTRASTON | NativeMethods.HCF_AVAILABLE;

                var ok = NativeMethods.SystemParametersInfo(NativeMethods.SPI.SPI_SETHIGHCONTRAST, NativeMethods.HIGHCONTRAST.GetSize(), ref hcf, NativeMethods.SPIF.SPIF_SENDCHANGE);
                if (!ok)
                    throw new Win32Exception();

            }
            if (!enable)
            {

                hcf.cbSize = NativeMethods.HIGHCONTRAST.GetSize();
                hcf.dwFlags = NativeMethods.HCF_AVAILABLE;

                var ok = NativeMethods.SystemParametersInfo(NativeMethods.SPI.SPI_SETHIGHCONTRAST, NativeMethods.HIGHCONTRAST.GetSize(), ref hcf, NativeMethods.SPIF.SPIF_SENDCHANGE);
                if (!ok)
                    throw new Win32Exception();
            }
        }
        #endregion

        #region Magnificador
        /// <summary>
        /// El sistema operativo ha de ser capaz de obviar la existencia del formulario
        /// y siga creando y renderizando, al completo, la imagen que queda debajo
        /// es decir; trabaja con un elemento opaco pero a la hora de pintar objetos
        /// hace como si fuera transparente
        /// </summary>
        public void SetLayeredTransparent(IntPtr handle, Point p, Size s)
        {
            if (
                NativeMethods.SetWindowLong(handle, NativeMethods.GWL_EXSTYLE,
                                            NativeMethods.WS_EX_LAYERED | NativeMethods.WS_EX_TRANSPARENT |
                                            NativeMethods.WS_EX_TOPMOST) == 0)
                throw new Win32Exception();

           if (NativeMethods.SetLayeredWindowAttributes(handle, 0, 255, NativeMethods.LWA_ALPHA) == 0)
                throw new Win32Exception();

            //Asignamos la propiedad TOPMOST para poner la lupa sobre los elementos de tipo toolTip,...
            NativeMethods.SetWindowPos(handle, NativeMethods.HWND_TOPMOST,
                                       p.X, p.Y, s.Width, s.Height, NativeMethods.SWP_NOACTIVATE);

        }

        public void ActualizaAmpliacion(IntPtr handle, Point p, Size s)
        {
            //Reasignamos la propiedad TOPMOST para poner la lupa sobre los elementos de tipo toolTip,...
            NativeMethods.SetWindowPos(handle, NativeMethods.HWND_TOPMOST,
                                       p.X,
                                       p.Y,
                                       s.Width,
                                       s.Height, NativeMethods.SWP_NOACTIVATE/* | NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE |
                                       NativeMethods.SWP_SHOWWINDOW*/);

        }        

        private Size GetImageSize(Size dimension, bool alisado, double factor)
        {
            var s = new Size();
            if (alisado)
            {
                s.Width = dimension.Width;
                s.Height = dimension.Height;
            }
            else
            {
                s.Width = (int)(dimension.Width / factor);
                s.Height = (int)(dimension.Height / factor);
            }
            return s;
        }

        
        public Image CreateImage(bool smooth, bool inverseColor, double factor, Size dimension, Point pSource)
        {
            Image img = null;
            IntPtr hBitmap = IntPtr.Zero;
            try
            {               
                hBitmap = NativeMethods.CreateCompatibleBitmap(hDcDesktop, dimension.Width, dimension.Height);
                NativeMethods.SelectObject(hDcLupa, hBitmap);

                var s = GetImageSize(dimension, smooth, factor);

                var sc = inverseColor ? NativeMethods.TernaryRasterOperations.NOTSRCCOPY : NativeMethods.TernaryRasterOperations.SRCCOPY;
                NativeMethods.StretchBlt(hDcLupa, 0, 0,
                                         dimension.Width, dimension.Height,
                                         hDcDesktop,
                                         pSource.X, pSource.Y,
                                         s.Width, s.Height,
                                         sc);

                if (hBitmap != IntPtr.Zero) 
                    img = Image.FromHbitmap(hBitmap);
            }
            catch (Exception ex)
            {
                //MessageBox.Show("Sobrecarga MaGUI: " + ex.ToString()); 
            }
            finally
            {
                //NativeMethods.ReleaseDC(desktop, hDcDesktop);
                //NativeMethods.DeleteDC(hDcLupa);
                NativeMethods.DeleteObject(hBitmap);

                GC.SuppressFinalize(this);
            }

            return img;
        }

        public Image CreateImageHorizontal(bool alisado, bool inverseColor, double factor, Size dimension, Point origen)
        {            
            IntPtr hDC_desktop = NativeMethods.GetWindowDC(NativeMethods.GetDesktopWindow());
            IntPtr hDC_lupa = NativeMethods.CreateCompatibleDC(hDC_desktop);
            IntPtr hBitmap = NativeMethods.CreateCompatibleBitmap(hDC_desktop, dimension.Width, dimension.Height);

            NativeMethods.SelectObject(hDC_lupa, hBitmap);

            var alto = (int)(dimension.Height / factor);
            var ancho = alisado ? dimension.Width : (int)(dimension.Width / factor);

            NativeMethods.TernaryRasterOperations sc = inverseColor ? NativeMethods.TernaryRasterOperations.NOTSRCCOPY : NativeMethods.TernaryRasterOperations.SRCCOPY;
            NativeMethods.StretchBlt(hDC_lupa, 0, 0,
                                     dimension.Width, dimension.Height,
                                     hDC_desktop,
                                     origen.X, origen.Y,
                                     ancho, alto,
                                     sc);

            try
            {
                var img = Image.FromHbitmap(hBitmap);

                NativeMethods.ReleaseDC(NativeMethods.GetDesktopWindow(), hDC_desktop);
                NativeMethods.DeleteDC(hDC_lupa);
                NativeMethods.DeleteObject(hBitmap);

                return img;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Sobrecarga MaGUI: " + ex.ToString());
                return null;
            }            
        }
       #endregion

        #region Aeros
        const uint DWM_EC_DISABLECOMPOSITION = 0;
        const uint DWM_EC_ENABLECOMPOSITION = 1;
 
        [DllImport("dwmapi.dll", EntryPoint = "DwmEnableComposition")]
        extern static uint DwmEnableComposition(uint compositionAction);
 
        /// &lt;summary&gt;
        /// Enable/Disable DWM composition (aka Aero)
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;enable&quot;&gt;True to enable composition, false to disable composition.&lt;/param&gt;
        /// &lt;returns&gt;True if the operation was successful.&lt;/returns&gt;
        public static bool EnableAero(bool enable)
        {
            try
            {
                if (enable)
                {
                    //DwmEnableComposition(DWM_EC_ENABLECOMPOSITION);
                }
                else
                {
                    DwmEnableComposition(DWM_EC_DISABLECOMPOSITION);
                }
 
                return true;
            }
            catch
            {
                return false;
            }
        }
        #endregion

    }
}